﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace DotNetCommon.Extensions;

/// <summary>
/// 提供IfNullUse系列扩展方法
/// </summary>
public static class IfNullUseExtensions
{
    #region 引用类型的IfNullUse
    /// <summary>
    /// 如果为null, 则返回 value, 如:
    /// <code>
    /// public class User { public int Id {get;set;} }
    /// User user = null;
    /// var id = user.IfNullUse(new User{ Id = 1}).Id; //相当于 var id = (user??new User{ Id = 1}).Id;
    /// </code>
    /// </summary>
    public static T IfNullUse<T>(this T obj, T value) where T : class => obj ?? value;

    private static Func<ConstructorInfo, ParameterInfo[]> _getParameters = null;
    private static readonly object _getParameters_lock = new();
    private static ConcurrentDictionary<Type, Func<object>> _newCaches = [];

    /// <summary>
    /// 如果为null, 则返回 new(), 如:
    /// <code>
    /// public class User { public int Id {get;set;} }
    /// User user = null; 
    /// var id = user.IfNullUseNew().Id; //相当于 var id = (user??new User()).Id;
    /// </code>
    /// </summary>
    public static T IfNullUseNew<T>(this T obj) where T : class
    {
        if (obj != null) return obj;
        var type = typeof(T);
        if (type.IsAbstract)
        {
            if (type.IsSealed) throw new Exception($"不能对静态类: {type.GetClassFullName()} 使用 new()!");
            else if (type.IsSealed) throw new Exception($"不能对抽象类: {type.GetClassFullName()} 使用 new()!");
        }
        //if (type.IsArray) type = type.MakeArrayType();//不需要
        if (type.IsInterface)
        {
            //只处理几种常用的接口
            var reflect = type.GetClassGenericFullName();
            switch (reflect.Name)
            {
                //字典
                case "System.Collections.Generic.IDictionary<TKey, TValue>":
                    {
                        type = typeof(Dictionary<,>).MakeGenericType(reflect.GenericTypes[0].type, reflect.GenericTypes[1].type);
                        break;
                    }
                case "System.Collections.IDictionary":
                    {
                        type = typeof(Dictionary<,>).MakeGenericType(typeof(object), typeof(object));
                        break;
                    }
                //集合
                case "System.Collections.Generic.IList<T>":
                case "System.Collections.Generic.ICollection<T>":
                    {
                        type = typeof(List<>).MakeGenericType(reflect.GenericTypes[0].type);
                        break;
                    }
                case "System.Collections.IList":
                case "System.Collections.ICollection":
                    {
                        type = typeof(ArrayList);
                        break;
                    }
                default:
                    {
                        throw new Exception($"不能对接口: {type.GetClassFullName()} 使用 new()!");
                    }
            }
        }
        //不需要考虑这种: int?, 因为 int? i=null; i.IfNullUseNew() 编译会报错
        var func = _newCaches.GetOrAdd(type, type =>
          {
              initGetParameters();
              var ctors = type.GetConstructors();
              if (ctors.Length == 0) throw new Exception($"类型 [{type.GetClassFullName()}] 没有构造函数, 无法使用 new()!");
              var pairs = new List<(ConstructorInfo ctor, ParameterInfo[] paras, int hasNoDefaultValueCount)>(ctors.Length);
              for (var i = 0; i < ctors.Length; i++)
              {
                  var ctor = ctors[i];
                  var paras = _getParameters(ctor);
                  if (paras.Length == 0)
                  {
                      //如果有空参构造函数,直接使用即可
                      return Expression.Lambda<Func<object>>(Expression.New(ctor)).Compile();
                  }
                  //加入备选
                  pairs.Add((ctor, paras, paras.Count(i => !i.HasDefaultValue)));
              }
              //没有默认值的参数越少越好, 参数的数量越少越好
              var best = pairs.OrderBy(i => i.paras.Length).OrderBy(i => i.hasNoDefaultValueCount).First();
              var expParas = best.paras.Select(i =>
              {
                  //有默认值设置就使用默认值,否则使用 default
                  if (i.HasDefaultValue) return Expression.Convert(Expression.Constant(i.DefaultValue), i.ParameterType);
                  return Expression.Default(i.ParameterType) as Expression;
              }).ToArray();
              return Expression.Lambda<Func<object>>(Expression.New(best.ctor, expParas)).Compile();
          });
        return (T)func();
    }
    private static void initGetParameters()
    {
        if (_getParameters == null)
        {
            lock (_getParameters_lock)
            {
                if (_getParameters == null)
                {
                    var tt = typeof(ConstructorInfo).BaseType;
                    var method = tt.GetMethod("GetParameters");
                    var para = Expression.Parameter(typeof(ConstructorInfo));
                    _getParameters = Expression.Lambda<Func<ConstructorInfo, ParameterInfo[]>>(Expression.Call(para, method), para).Compile();
                }
            }
        }
    }
    #endregion

    #region 非引用类型的 IfNullOrDefaultUse & IfDefaultUse
    /// <summary>
    /// 如果是 null 的话返回 value
    /// </summary>
    public static T IfNullUse<T>(this T? obj, T value) where T : struct => (obj == null) ? value : obj.Value;
    /// <summary>
    /// 如果是 null 或 默认值 的话返回 value
    /// </summary>
    public static T IfNullOrInitValueUse<T>(this T? obj, T value) where T : struct => (obj == null || obj.Value.Equals(default(T))) ? value : obj.Value;

    /// <summary>
    /// 如果是 默认值 的话返回 value
    /// </summary>
    public static T IfDefaultUse<T>(this T obj, T value) where T : struct => (obj.Equals(default(T))) ? value : obj;
    #endregion

    #region string 的 IfNullOrEmptyUse & IfNullOrWhiteSpaceUse & IfEmptyUseNull
    /// <summary>
    /// 如果字符串为 null 或者 空字符串,则使用备选值
    /// </summary>
    public static string IfNullOrEmptyUse(this string str, string value)
    {
        return string.IsNullOrEmpty(str) ? value : str;
    }
    /// <summary>
    /// 如果字符串为 null 或者 空字符串 或者全部是空白符,则使用备选值
    /// </summary>
    public static string IfNullOrWhiteSpaceUse(this string str, string value)
    {
        return string.IsNullOrWhiteSpace(str) ? value : str;
    }
    #endregion
}
